# -*- coding: utf-8 -*-
# @Time    : 2024/6/19 16:29
# @Author  : yujiahao
# @File    : 32_except.py
# @description:Python 中的异常处理


# todo 一、异常和错误

'''

一、错误

    1 程序中难免出现错误，而错误分成两种
        1.语法错误（这种错误，根本过不了Python解释器的语法检测，必须在程序执行前就改正）

            #语法错误示范一
                if

            #语法错误示范二
                def test:
                    pass

            #语法错误示范三
                print(haha

    2.逻辑错误（逻辑错误）

        #用户输入不完整(比如输入为空)或者输入非法(输入不是数字)
        num=input(">>: ")
        int(num)

        #无法完成计算
        res1 = 1/0
        res2 = 1+'str'

二、 异常

    【什么是异常】

        异常就是程序运行时发生错误的信号，在我们语法上是没有问题,但是运行时就报错的称为异常, 异常发生之后剩余代码将不会继续执行

        比如直接打印一个未被定义过得值：print(s)
        则控制台输出如下信息：

            【追踪的信息】
                Traceback (most recent call last):
              File "/Users/yujiahao/02_Xizhi/日常学习（私人勿动）/code/hi_python/com/yujiahao/my_python/32_except.py", line 46, in <module>
                print(s)
                      ^

            【错误类】   【错误的值】
            NameError: name 's' is not defined


    【什么是异常处理】

        Python解释器检测到错误，触发异常（也允许程序员自己触发异常raise）

        程序员编写特定的代码，专门用来捕捉这个异常（这段代码与程序逻辑无关，与异常处理有关）

        如果捕捉成功则进入另外一个处理分支，执行你为其定制的逻辑，使程序不会崩溃，这就是异常处理

    【为什么要进行异常处理】

        Python解析器去执行程序，检测到了一个错误时，触发异常，异常触发后且没被处理的情况下，程序就在当前异常处终止，后面的代码不会运行，
        所以你必须提供一种异常处理机制来增强你程序的健壮性与容错性。

    【如何进行异常处理】

        首先须知，异常是由程序的错误引起的，语法上的错误跟异常处理是无关，语法错误必须在程序运行前就修正

        处理异常的两种方式:

            流程控制语句(if)
            异常处理 -- "私人订制"


三、Python中的异常种类

        代码中的错误举例：

            # 触发IndexError: 索引超出范围
            l=['meet','aa']
            l[3]

            # 触发KeyError: 字典中没有此键
            dic={'name':'egon'}
            dic['age']

            #触发ValueError: 字符串类型不能进行转换
            s='hello'
            int(s)
        ------------------------------------------------------------
        常用异常类型及描述
        ------------------------------------------------------------
        异常类型                          异常描述
        ------------------------------------------------------------
        AssertionError             - 断言语句（assert）失败
        AttributeError             - 试图访问一个对象没有的属性
        EOFError                   - 输入意外结束（文件末尾）
        Exception                  - 常规错误的基类
        ImportError                - 无法引入模块或包
        IndentationError           - 代码没有正确对齐
        IndexError                 - 下标索引超出序列边界
        IOError                    - 输入/输出操作失败
        KeyboardInterrupt          - 用户中断执行（通常是输入^C）
        KeyError                   - 试图访问字典里不存在的键
        NameError                  - 使用未赋值的变量
        NotImplementedError        - 尚未实现的方法
        OSError                    - 操作系统错误
        RuntimeError               - 一般的运行时错误
        StopIteration              - 迭代器没有更多的值
        SyntaxError                - Python遇到非法代码
        TabError                   - Tab和空格混用
        TypeError                  - 传入对象类型与要求不符
        UnboundLocalError          - 访问未设置的局部变量
        UnicodeDecodeError         - Unicode解码错误
        UnicodeEncodeError         - Unicode编码错误
        ValueError                 - 传入值不符合要求
        ZeroDivisionError          - 除零错误
        ------------------------------------------------------------

        不常用异常类型及描述
        ------------------------------------------------------------
        异常类型                          异常描述
        ------------------------------------------------------------
        ArithmeticError            - 所有数值计算错误的基类
        BaseException              - 所有异常的基类
        BufferError                - 与缓冲区相关的操作引发的异常
        BytesWarning               - 与字节相关的警告
        DeprecationWarning         - 关于已弃用功能的警告
        EnvironmentError           - 操作系统相关的基类异常（已被OSError取代）
        FloatingPointError         - 浮点运算错误
        FutureWarning              - 关于将来语义改变的警告
        GeneratorExit              - 生成器（generator）退出
        ImportWarning              - 与模块导入相关的警告
        LookupError                - 查找序列时引发的基类异常
        MemoryError                - 内存溢出错误
        OverflowError              - 数值运算超出最大限制
        PendingDeprecationWarning  - 功能将来会被弃用的警告
        ReferenceError             - 弱引用访问已被回收的对象
        RuntimeWarning             - 可疑的运行时行为的警告
        StandardError              - 所有内建标准异常的基类（已被Exception取代）
        SyntaxWarning              - 可疑的语法警告
        SystemError                - 解释器系统错误
        SystemExit                 - 解释器请求退出
        UnicodeError               - Unicode相关的错误
        UnicodeTranslateError      - Unicode翻译错误
        UnicodeWarning             - 与Unicode相关的警告
        UserWarning                - 用户代码生成的警告
        Warning                    - 警告的基类
        ------------------------------------------------------------


'''

# todo 1.1、 流程控制语句


# 使用if判断进行异常处理

num1 = input('>>: ')  # 输入一个字符串试试

if num1.isdigit():
    int(num1)  # 我们的主要目的是进行类型转换,其余的都属于异常处理范畴

elif num1.isspace():
    print('输入的是空格,就执行我这里的逻辑')

elif len(num1) == 0:
    print('输入的是空,就执行我这里的逻辑')

else:
    print('其他情情况,执行我这里的逻辑')

'''

问题一： 使用if 的方式我们只为第一段代码加上了异常处理，但这些if，跟你的代码逻辑并无关系，这样你的代码会因为可读性差而不容易被看懂

问题二： 这只是我们代码中的一个小逻辑，如果类似的逻辑多，那么每一次都需要判断这些内容，就会倒置我们的代码特别冗长。

总结：

    1.if判断式的异常处理只能针对某一段代码，对于不同的代码段的相同类型的错误你需要写重复的if来进行处理。
    
    2.在你的程序中频繁的写与程序本身无关，与异常处理有关的if，会使得你的代码可读性极其的差
    
    3.if是可以解决异常的，只是存在1,2的问题，所以，千万不要妄下定论 if 不能用来异常处理
'''

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 二、 自动抛出异常


"""
 Python, Java 和 Scala 中的异常处理

1. Python 中的异常处理
    
   1.0 语法：
       
       try:
        # 被检测的代码块
    except 异常类型:
        # try中一旦检测到异常，就执行这个位置的逻辑
    finally: 
        // 我一定会被执行
    
   1.1 关键字:
       - `try`: 包含可能引发异常的代码块。
       - `except`: 捕获并处理异常。
       - `else`: 如果没有引发异常，则执行此代码块。
       - `finally`: 无论是否引发异常，都会执行此代码块。

   1.2 示例代码:

   try:
       # 尝试执行可能引发异常的代码
       result = 10 / 0
   except ZeroDivisionError as e:
       # 捕获并处理除零错误异常
       print(f"捕获到异常: {e}")
   else:
       # 如果没有引发异常，则执行此代码块
       print("没有异常发生")
   finally:
       # 无论是否引发异常，都会执行此代码块
       print("无论是否发生异常，都会执行")

   1.3 详细说明:
       - `try` 块中包含可能引发异常的代码。
       - `except` 块捕获特定类型的异常，并处理该异常。
       - `else` 块在没有引发异常时执行。
       - `finally` 块无论是否引发异常都会执行，常用于清理代码。

   1.4 注意事项:
       - 可以有多个 `except` 块来捕获不同类型的异常。
       - `else` 块是可选的，用于在没有异常时执行特定代码。
       - `finally` 块通常用于释放资源，例如关闭文件或网络连接。

2. Java 中的异常处理

   2.0 语法：
   
       try {
        // 被检测的代码块
    } catch (异常类型 e) {
        // try中一旦检测到异常，就执行这个位置的逻辑
    } finally {
        // 我一定会被执行
    }

   2.1 关键字:
       - `try`: 包含可能引发异常的代码块。
       - `catch`: 捕获并处理异常。
       - `finally`: 无论是否引发异常，都会执行此代码块。
       - `throw`: 手动引发异常。
       - `throws`: 方法声明中用来指明该方法可能会抛出的异常。

   2.2 示例代码:

   public class ExceptionExample {
       public static void main(String[] args) {
           try {
               // 尝试执行可能引发异常的代码
               int result = 10 / 0;
           } catch (ArithmeticException e) {
               // 捕获并处理算术异常（如除零错误）
               System.out.println("捕获到异常: " + e);
           } finally {
               // 无论是否引发异常，都会执行此代码块
               System.out.println("无论是否发生异常，都会执行");
           }
       }
   }

   2.3 详细说明:
       - `try` 块中包含可能引发异常的代码。
       - `catch` 块捕获特定类型的异常，并处理该异常。
       - `finally` 块无论是否引发异常都会执行，常用于释放资源。
       - `throw` 关键字用于手动引发异常。
       - `throws` 关键字用于方法声明，指明该方法可能会抛出的异常类型。

   2.4 注意事项:
       - 一个 `try` 块可以有多个 `catch` 块来处理不同类型的异常。
       - `finally` 块通常用于释放资源，例如关闭文件或网络连接。
       - 使用 `throw` 关键字可以手动引发异常，通常在异常条件下使用。
       - 使用 `throws` 关键字声明方法可能抛出的异常，调用该方法时需要处理这些异常。

3. Scala 中的异常处理
   
   3.0 语法：
   
       try {
        // 被检测的代码块
    } catch {
        case e: 异常类型 =>
            // try中一旦检测到异常，就执行这个位置的逻辑
    } finally {
        // 我一定会被执行
    }


   3.1 关键字:
       - `try`: 包含可能引发异常的代码块。
       - `catch`: 捕获并处理异常。Scala 的 `catch` 块使用模式匹配来处理不同的异常。
       - `finally`: 无论是否引发异常，都会执行此代码块。

   3.2 示例代码:

   object ExceptionExample {
       def main(args: Array[String]): Unit = {
           try {
               // 尝试执行可能引发异常的代码
               val result = 10 / 0
           } catch {
               // 使用模式匹配捕获并处理算术异常（如除零错误）
               case e: ArithmeticException => println(s"捕获到异常: ${e.getMessage}")
           } finally {
               // 无论是否引发异常，都会执行此代码块
               println("无论是否发生异常，都会执行")
           }
       }
   }

   3.3 详细说明:
       - `try` 块中包含可能引发异常的代码。
       - `catch` 块使用模式匹配来捕获并处理不同类型的异常。
       - `finally` 块无论是否引发异常都会执行，常用于清理代码。

   3.4 注意事项:
       - Scala 的 `catch` 块使用模式匹配来处理不同类型的异常，这与 Java 和 Python 的 `catch`/`except` 块有所不同。
       - `finally` 块通常用于清理资源，例如关闭文件或网络连接。
       - Scala 没有 `throws` 关键字，但可以使用 `throw` 关键字手动引发异常。

通过这些代码示例和详细说明，可以更好地理解 Python、Java 和 Scala 中的异常处理机制。
"""

# todo 2.1 基本语法


# 我们读取一下文件试试看

input_path = '../../../input/美女模特空姐护士联系方式.txt'

f = open(input_path, "r", encoding="utf-8")

while True:
    try:
        '''
        next(f)会触发迭代f，依次next(f)就可以读取文件的一行行的内容，无论文件userinf有多大，同一时刻内存中只有一行内容。
        当文件句柄f中的内容next完后就会抛出异常StopIteration后才可以执行f.close()
        '''
        print(next(f))
    except StopIteration:
        f.close()
        break
    finally:
        print('我无论如何都要执行')

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 2.2 异常类处理指定的异常情况

'''报什么错,用什么错误类型去接受'''

# 虽然有异常处理，但是未正确捕获到异常，程序直接报错：except 语句捕获的是 IndexError 异常，但实际代码中会引发 ValueError 异常

s1 = 'hello'
try:
    int(s1)

except IndexError as e:  # as相当于将报错信息赋值给了e
    print(e)
# 下面是通用异常，不在这个案例说明之内
except Exception as a:
    print('这行捕获异常代码要注释掉，不算在此说明案例中，纯粹为了下文不报错')

# 正确方法:
s1 = 'hello'
try:
    int(s1)
except ValueError as e:
    print(e)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 2.3 万能异常 - Exception

'''
如果你统一用Exception，没错，是可以捕捉所有异常，但意味着你在处理所有异常时都使用同一个逻辑去处理（这里说的逻辑即当前expect下面跟的代码块）
'''

s1 = 'hello'
try:
    int(s1)
except Exception as e:
    """丢弃或者执行其他逻辑"""
    print("编码逻辑")

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 2.4 多分支

s1 = 'hello'
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
except Exception as e:
    print(e)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 2.5 异常的其他结构
s1 = 'hello'
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)

else:
    print('try内代码块没有异常则执行我')

finally:
    print('无论异常与否,都会执行该模块,通常是进行清理工作')

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 三、手动抛出异常

"""
### 手动抛出异常在 Python、Java 和 Scala 中的用法

一、Python 中手动抛出异常

    1. 用途
        
        `raise` 关键字用于手动引发异常。通过引发异常，可以在程序运行时指出特定的错误条件，并中断正常的程序执行流程，以便进行错误处理或调试。

    2. 用法
      
       `raise` 关键字的基本用法是指定要引发的异常类型。可以引发内置的异常类型，也可以引发自定义的异常类型。
    
       1> 基本用法:
         raise ExceptionType("异常消息")
    
       - 示例代码:
         def divide(a, b):
             if b == 0:
                 # 手动引发除零错误异常
                 raise ZeroDivisionError("除数不能为零")
             return a / b
    
         try:
             result = divide(10, 0)
         except ZeroDivisionError as e:
             # 捕获并处理除零错误异常
             print(f"捕获到异常: {e}")
         else:
             # 如果没有引发异常，则执行此代码块
             print(f"结果是: {result}")
         finally:
             # 无论是否引发异常，都会执行此代码块
             print("无论是否发生异常，都会执行")
    
       2> 自定义异常:
        
         可以通过继承内置的异常类来自定义异常类型，并使用 `raise` 关键字引发自定义异常。
    
         示例代码:
         class CustomError(Exception):
             pass
    
         def check_value(value):
             if value < 0:
                 # 手动引发自定义异常
                 raise CustomError("值不能为负数")
             return value
    
         try:
             result = check_value(-1)
         except CustomError as e:
             # 捕获并处理自定义异常
             print(f"捕获到异常: {e}")
         else:
             # 如果没有引发异常，则执行此代码块
             print(f"结果是: {result}")
         finally:
             # 无论是否引发异常，都会执行此代码块
             print("无论是否发生异常，都会执行")
    
       3> 重新引发异常:
      
         在 `except` 块中，可以使用 `raise` 关键字重新引发捕获到的异常，以便在更高层级进行处理。
    
         示例代码:
         def func():
             try:
                 # 手动引发异常
                 raise ValueError("一个值错误")
             except ValueError as e:
                 print(f"在 func 中捕获到异常: {e}")
                 # 重新引发异常
                 raise
    
         try:
             func()
         except ValueError as e:
             print(f"在主程序中捕获到异常: {e}")
    
    3. 注意事项
       - 异常类型: `raise` 关键字后面必须跟随一个异常类型或异常实例。异常类型通常是内置的异常类或自定义的异常类。
       - 异常消息: 可以在引发异常时提供一个异常消息，以便更好地描述错误情况。
       - 异常链: 在 Python 3 中，可以使用 `raise` 关键字结合 `from` 关键字来创建异常链，以便在捕获一个异常并引发另一个异常时保留原始异常的信息。
    
         示例代码:
         try:
             1 / 0
         except ZeroDivisionError as e:
             raise ValueError("处理除零错误时出错") from e

二、 Java 中手动抛出异常

    1. 用途
       在 Java 中，异常处理用于捕获和处理程序运行时发生的错误情况。通过异常处理机制，可以在错误发生时执行特定的代码，从而避免程序崩溃。
    
    2. 用法
       Java 中的异常处理主要通过 try-catch-finally 语句来实现。可以捕获内置的异常类型，也可以捕获自定义的异常类型。
    
       - 基本用法:
         try {
             // 可能引发异常的代码
         } catch (ExceptionType e) {
             // 处理异常的代码
         } finally {
             // 无论是否发生异常，都会执行的代码
         }
    
       - 示例代码:
         public class Main {
             public static void main(String[] args) {
                 try {
                     int result = divide(10, 0);
                 } catch (ArithmeticException e) {
                     // 捕获并处理除零错误异常
                     System.out.println("捕获到异常: " + e.getMessage());
                 } finally {
                     // 无论是否引发异常，都会执行此代码块
                     System.out.println("无论是否发生异常，都会执行");
                 }
             }
    
             public static int divide(int a, int b) {
                 if (b == 0) {
                     // 手动引发除零错误异常
                     throw new ArithmeticException("除数不能为零");
                 }
                 return a / b;
             }
         }
    
       - 自定义异常:
         可以通过继承内置的异常类来自定义异常类型，并使用 throw 关键字引发自定义异常。
    
         示例代码:
         public class CustomException extends Exception {
             public CustomException(String message) {
                 super(message);
             }
         }
    
         public class Main {
             public static void main(String[] args) {
                 try {
                     checkValue(-1);
                 } catch (CustomException e) {
                     // 捕获并处理自定义异常
                     System.out.println("捕获到异常: " + e.getMessage());
                 } finally {
                     // 无论是否引发异常，都会执行此代码块
                     System.out.println("无论是否发生异常，都会执行");
                 }
             }
    
             public static void checkValue(int value) throws CustomException {
                 if (value < 0) {
                     // 手动引发自定义异常
                     throw new CustomException("值不能为负数");
                 }
             }
         }
    
    3. 注意事项
       - 异常类型: Java 中的异常分为受检异常（Checked Exception）和非受检异常（Unchecked Exception）。受检异常必须在方法签名中声明，或者在方法内部捕获处理。
       - 异常消息: 可以在引发异常时提供一个异常消息，以便更好地描述错误情况。
       - 异常链: 可以使用构造函数或 initCause() 方法来创建异常链，以便在捕获一个异常并引发另一个异常时保留原始异常的信息。
    
         示例代码:
         try {
             int result = 1 / 0;
         } catch (ArithmeticException e) {
             throw new RuntimeException("处理除零错误时出错", e);
         }

三、 Scala 中手动抛出异常
    
    1. 用途
        在 Scala 中，异常处理用于捕获和处理程序运行时发生的错误情况。通过异常处理机制，可以在错误发生时执行特定的代码，从而避免程序崩溃。
    
    2. 用法
       Scala 中的异常处理主要通过 try-catch-finally 语句来实现。可以捕获内置的异常类型，也可以捕获自定义的异常类型。
    
       - 基本用法:
         try {
             // 可能引发异常的代码
         } catch {
             case e: ExceptionType => // 处理异常的代码
         } finally {
             // 无论是否发生异常，都会执行的代码
         }
    
       - 示例代码:
         object Main extends App {
             try {
                 val result = divide(10, 0)
             } catch {
                 case e: ArithmeticException =>
                     // 捕获并处理除零错误异常
                     println(s"捕获到异常: ${e.getMessage}")
             } finally {
                 // 无论是否引发异常，都会执行此代码块
                 println("无论是否发生异常，都会执行")
             }
    
             def divide(a: Int, b: Int): Int = {
                 if (b == 0) {
                     // 手动引发除零错误异常
                     throw new ArithmeticException("除数不能为零")
                 }
                 a / b
             }
         }
    
       - 自定义异常:
         可以通过继承内置的异常类来自定义异常类型，并使用 throw 关键字引发自定义异常。
    
         示例代码:
         class CustomException(message: String) extends Exception(message)
    
         object Main extends App {
             try {
                 checkValue(-1)
             } catch {
                 case e: CustomException =>
                     // 捕获并处理自定义异常
                     println(s"捕获到异常: ${e.getMessage}")
             } finally {
                 // 无论是否引发异常，都会执行此代码块
                 println("无论是否发生异常，都会执行")
             }
    
             def checkValue(value: Int): Unit = {
                 if (value < 0) {
                     // 手动引发自定义异常
                     throw new CustomException("值不能为负数")
                 }
             }
         }
    
    3. 注意事项
       
       - 异常类型: 在 Scala 中，异常处理与 Java 类似，使用 try-catch-finally 语句。
       - 异常消息: 可以在引发异常时提供一个异常消息，以便更好地描述错误情况。
       - 异常链: 可以使用构造函数或 initCause() 方法来创建异常链，以便在捕获一个异常并引发另一个异常时保留原始异常的信息。
    
         示例代码:
         try {
             val result = 1 / 0
         } catch {
             case e: ArithmeticException =>
                 throw new RuntimeException("处理除零错误时出错", e)
         }
   
   
   【注意】
   
        Python
            - raise 关键字用于手动引发异常。
            - 可以引发内置异常（如 ZeroDivisionError）或自定义异常。
            - 可以在 raise 后面添加异常实例，包含异常消息。
        Java
            - throw 关键字用于手动引发异常。
            - 可以引发内置异常（如 ArithmeticException）或自定义异常。
            - 在方法声明中使用 throws 关键字指明该方法可能会抛出的异常类型。
        Scala
            - throw 关键字用于手动引发异常。
            - 可以引发内置异常（如 ArithmeticException）或自定义异常。
            - throw 后面跟随异常实例，包含异常消息。
"""

# todo 3.1 主动触发异常
try:
    raise TypeError('我是手动触发的异常')
except Exception as e:
    print(e)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


# todo 3.2、 自定义异常
class EvaException(BaseException):
    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg


try:
    raise EvaException('我是自定义异常')
except EvaException as e:
    print(e)

# todo 四、断言

"""
断言（assert）的用法、注意事项和应用场景

Python 中的断言
        
        1. 用法
        在 Python 中，assert 语句用于检查一个条件是否为真。如果条件为假，程序会引发一个 AssertionError 异常。
        
        基本语法如下：
           assert condition, "error_message"
        
        - condition 是一个表达式，返回值应为布尔类型。
        - error_message 是一个可选的字符串，当断言失败时会显示。
        
        2. 注意事项
        1. 仅在开发和测试阶段使用：
          断言主要用于捕获开发过程中的逻辑错误和不一致性，通常不应在生产代码中依赖断言进行错误处理。
        
        2. 避免副作用：
          断言语句不应改变程序的状态或产生副作用。它们的作用应仅限于验证条件。
        
        3. 启用和禁用：
          在生产环境中，Python 可以通过运行时选项 -O（优化模式）禁用断言。这会移除所有的断言语句。
          例如，运行 python -O script.py 会禁用 script.py 中的所有断言。
        
        4. 错误消息：
          提供有意义的错误消息有助于调试。当断言失败时，错误消息会显示在异常信息中。
        
        3. 应用场景
        1. 验证函数输入：
          使用断言验证函数参数是否满足预期条件。
        
        2. 检查不变量：
          在程序的关键部分使用断言检查不变量（即在程序执行过程中保持不变的条件）。
        
        3. 单元测试：
          在测试代码中使用断言验证函数的输出是否符合预期。
        
        4. 简单例子
        
        示例 1：验证函数输入
        def divide(a, b):
           assert b != 0, "除数不能为零"
           return a / b
        
        print(divide(10, 2))  # 正常输出 5.0
        print(divide(10, 0))  # 引发 AssertionError: 除数不能为零
        
        示例 2：检查不变量
        def process_data(data):
           # 假设 data 应该是非空列表
           assert isinstance(data, list) and len(data) > 0, "数据应该是非空列表"
           # 处理数据
           return [x * 2 for x in data]
        
        print(process_data([1, 2, 3]))  # 正常输出 [2, 4, 6]
        print(process_data([]))        # 引发 AssertionError: 数据应该是非空列表
        
        示例 3：单元测试
        def add(a, b):
           return a + b
        
        def test_add():
           assert add(2, 3) == 5, "2 + 3 应该等于 5"
           assert add(-1, 1) == 0, "-1 + 1 应该等于 0"
           assert add(0, 0) == 0, "0 + 0 应该等于 0"
        
        # 运行测试
        test_add()
        print("所有测试通过")
   
 Java 中的断言
   在 Java 中，assert 关键字用于进行断言检查。默认情况下，断言是禁用的，可以通过运行时参数启用。

   使用方法：
   1. 基本语法：
      assert condition;
      assert condition : errorMessage;

   2. 启用断言：
      - 通过命令行参数 -ea 或 -enableassertions 启用断言。
      - 例如，运行 java -ea MyClass 会启用 MyClass 中的断言。

   示例：
   public class AssertExample {
       public static void main(String[] args) {
           int a = 5;
           int b = 0;
           assert b != 0 : "除数不能为零";
           System.out.println(a / b);
       }
   }

   运行此代码时，如果断言被启用且 b == 0，则会抛出 AssertionError 并显示消息 "除数不能为零"。

 Scala 中的断言
   在 Scala 中，可以使用 assert 方法进行断言检查。Scala 的断言在编译时和运行时都可以使用。

   使用方法：
   1. 基本语法：
      assert(condition)
      assert(condition, errorMessage)

   2. Scala 的断言默认是启用的。

   示例：
   object AssertExample {
     def main(args: Array[String]): Unit = {
       val a = 5
       val b = 0
       assert(b != 0, "除数不能为零")
       println(a / b)
     }
   }

   运行此代码时，如果 b == 0，则会抛出 AssertionError 并显示消息 "除数不能为零"。

总结：
在 Java 和 Scala 中，断言用于在开发和测试阶段捕获逻辑错误和不一致性。Java 中需要显式启用断言，而 Scala 中默认启用断言。两者的基本语法和用法比较类似，都可以用于验证条件并在条件不满足时抛出错误消息。

"""

# todo 4.1 使用 1
''' 
assert 语句用于进行断言，断言表达式应该为真。如果断言失败（即表达式为假），则会引发 AssertionError 异常。
    
    # 断言表达式为真
    assert 1 == 1  # 这个断言会通过，因为 1 确实等于 1
    
    # 断言表达式为假
    assert 1 == 2  # 这个断言会失败，因为 1 不等于 2
当第二个 assert 语句执行时，表达式 1 == 2 为假，Python 会引发一个 AssertionError 异常。
你可以通过捕获这个异常来处理断言失败的情况。以下是一个示例：
'''

try:
    assert 1 == 1, "1 应该等于 1"
    print("第一个断言通过")

    assert 1 == 2, "1 不应该等于 2"
    print("第二个断言通过")
except AssertionError as e:
    print(f"断言失败: {e}")


# todo 4.2 使用 2

def add(a, b):
    """
    返回两个数的和
    """
    return a + b


def test_add():
    """
    测试 add 函数
    """
    # 基本断言
    try:
        assert add(1, 1) == 2, "1 + 1 应该等于 2"
    except AssertionError as e:
        print(f"断言失败: {e}")

    try:
        assert add(-1, 1) == 0, "-1 + 1 应该等于 0"
    except AssertionError as e:
        print(f"断言失败: {e}")

    try:
        assert add(0, 0) == 0, "0 + 0 应该等于 0"
    except AssertionError as e:
        print(f"断言失败: {e}")

    # 带有错误消息的断言
    try:
        assert add(1, 2) == 3, "1 + 2 应该等于 3"
    except AssertionError as e:
        print(f"断言失败: {e}")

    try:
        assert add(-1, -1) == -2, "-1 + -1 应该等于 -2"
    except AssertionError as e:
        print(f"断言失败: {e}")


def divide(a, b):
    """
    返回两个数的商
    """
    assert b != 0, "除数不能为零"
    return a / b


def test_divide():
    """
    测试 divide 函数
    """
    # 基本断言
    try:
        assert divide(10, 2) == 5, "10 / 2 应该等于 5"
    except AssertionError as e:
        print(f"断言失败: {e}")

    try:
        assert divide(9, 3) == 3, "9 / 3 应该等于 3"
    except AssertionError as e:
        print(f"断言失败: {e}")

    # 带有错误消息的断言
    try:
        divide(10, 0)
    except AssertionError as e:
        print(f"捕获到异常: {e}")
        try:
            assert str(e) == "除数不能为零", f"断言错误消息应该是 '除数不能为零'，但实际是 '{e}'"
        except AssertionError as ae:
            print(f"断言失败: {ae}")


test_add()
print("test_add 测试通过")

test_divide()
print("test_divide 测试通过")

# todo 五、总结

'''
1、try..except的方式比较if的方式的好处
    
    try..except这种异常处理机制就是取代if那种方式，让你的程序在不牺牲可读性的前提下增强健壮性和容错性

    异常处理中为每一个异常定制了异常类型（Python中统一了类与类型，类型即类），对于同一种异常，一个except就可以捕捉到，可以同时处理多段代码的异常（无需‘写多个if判断式’）减少了代码，增强了可读性

2、使用try..except的方式好处

    1：把错误处理和真正的工作分开来 ；
    2：代码更易组织，更清晰，复杂的工作任务更容易实现；
    3：毫无疑问，更安全了，不至于由于一些小的疏忽而使程序意外崩溃了；
    
3、什么时候用异常处理

    try...except应该尽量少用，因为它本身就是你附加给你的程序的一种异常处理的逻辑，与你的主要的工作是没有关系的，
    这种东西加的多了，会导致你的代码可读性变差，只有在有些异常无法预知的情况下，才应该加上try...except，其他的逻辑错误应该尽量修正。
'''
